home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / WAIS / next-ui / WaisSource.m < prev    next >
Encoding:
Text File  |  1992-02-03  |  10.3 KB  |  408 lines

  1. // WaisSource.m
  2. //
  3. // Free software created 1 Feb 1992
  4. // by Paul Burchard <burchard@math.utah.edu>.
  5. // Incorporating:
  6. /* 
  7.    WIDE AREA INFORMATION SERVER SOFTWARE:
  8.    No guarantees or restrictions.  See the readme file for the full standard
  9.    disclaimer.
  10.  
  11.    This is part of the [NeXTstep] user-interface for the WAIS software.
  12.    Do with it as you please.
  13.  
  14.    Version 0.82
  15.    Wed Apr 24 1991
  16.  
  17.    jonathan@Think.COM
  18.  
  19. */
  20. //
  21.  
  22. #import "WaisSource.h"
  23.  
  24. // Search path for sources.
  25. static id sourceFolderList;
  26.  
  27. // Error panel title.
  28. static char *errorTitle = "WAIS Source Error!";
  29.  
  30. // User information sent to remote servers.
  31. static NXAtom userInfo;
  32.  
  33. // Decoders for WAIS structured files.
  34. _WaisDecoder waisDataFileDecoder[] = 
  35. {
  36.     { ":data-file",    W_FIELD,0,0,    ReadString,3,    WriteString,2, 
  37.                         MAX_SYMBOL_SIZE },
  38.     { ":index-mode",    W_FIELD,0,0,    ReadSymbol,3,    WriteSymbol,2, 
  39.                         MAX_SYMBOL_SIZE },
  40.     { NULL }
  41. };
  42.  
  43. _WaisDecoder waisSourceDecoder[] = 
  44. {
  45.     { ":version",    W_FIELD,0,0,    ReadLongS,2,    WriteLongS,2 },
  46.     { ":ip-name",    W_FIELD,0,0,    ReadString,3,    WriteString,2, 
  47.                         MAX_SYMBOL_SIZE },
  48.     { ":ip-address",    W_FIELD,0,0,    ReadString,3,    WriteString,2, 
  49.                         MAX_SYMBOL_SIZE },
  50.     { ":configuration",    W_FIELD,0,0,    ReadString,3,    NULL,0,
  51.                         MAX_SYMBOL_SIZE },
  52.     { ":tcp-port",    W_FIELD,0,0,    ReadLongS,2,    WriteLongS,2 },
  53.     { ":cost",        W_FIELD,0,0,    ReadDoubleS,2,    WriteDoubleS,2 },
  54.     { ":cost-unit",    W_FIELD,0,0,    ReadSymbol,3,    WriteSymbol,2, 
  55.                         MAX_SYMBOL_SIZE },
  56.     { ":database-name",    W_FIELD,0,0,    ReadString,3,    WriteString,2, 
  57.                         MAX_SYMBOL_SIZE },
  58.     { ":description",    W_FIELD,0,0,    ReadString,3,    WriteString,2, 
  59.                         READ_BUF_SIZE },
  60.     { ":maintainer",    W_FIELD,0,0,    ReadString,3,    WriteString,2, 
  61.                         MAX_SYMBOL_SIZE },
  62.     { ":update-time",    W_FIELD,0,0,    ReadListX,1,    NULL,0 },//!!!
  63.     { ":index-list",    W_LIST,
  64.         ":include",    waisDataFileDecoder },
  65.     { NULL }
  66. };
  67.  
  68.  
  69. @implementation WaisSource
  70.  
  71. + folderList
  72. {
  73.     return sourceFolderList;
  74. }
  75.  
  76. + setFolderList:aList
  77. {
  78.     if(sourceFolderList) [sourceFolderList free];
  79.     sourceFolderList = aList;
  80.     return self;
  81. }
  82.  
  83. + (const char *)defaultHomeFolder
  84. {
  85.     return "/Library/WAIS/sources";
  86. }
  87.  
  88. + (const char *)fileStructName
  89. {
  90.     return ":source";
  91. }
  92.  
  93. + (WaisDecoder)fileStructDecoder
  94. {
  95.     return waisSourceDecoder;
  96. }
  97.  
  98. + (const char *)errorTitle
  99. {
  100.     return errorTitle;
  101. }
  102.  
  103. + registerUser:(const char *)userInfoString
  104. {
  105.     userInfo = NXUniqueString(userInfoString);
  106.     return self;
  107. }
  108.  
  109. + (BOOL)checkFileName:(const char *)fileName
  110. {
  111.     if(!fileName) return NO;
  112.     if(strlen(fileName) <= strlen(W_S_EXT)) return NO;
  113.     if(!strstr(fileName, W_S_EXT)) return NO;
  114.     if(0 != strcmp(W_S_EXT, strstr(fileName, W_S_EXT))) return NO;
  115.     return YES;
  116. }
  117.  
  118. - setKey:(const char *)aKey
  119. {
  120.     const char *tail;
  121.  
  122.     // Set ":filename" field to be consistent with new key.
  123.     // This is so source can function as its own source-id.
  124.     if(![super setKey:aKey]) return nil;
  125.     if(![self key]) return nil;
  126.     tail = strrchr([self key], '/');
  127.     if(tail) tail++;
  128.     else tail = [self key];
  129.     [self insertStringKey:":filename" value:tail];
  130.     return self;
  131. }
  132.  
  133. - initKey:(const char *)aKey
  134. {
  135.     [super initKey:aKey];
  136.     dataFileList = [[List alloc] init];
  137.  
  138.     // Redundant...
  139.     isConnected = NO;
  140.     connection = NULL;
  141.     bufferLength = 0;
  142.     listCounter = 0;
  143.     return self;
  144. }
  145.  
  146. - free
  147. {
  148.     // If no other source needs our connection, close it.
  149.     [self setConnected:NO];
  150.     [[dataFileList freeObjects] free];
  151.     return [super free];
  152. }
  153.  
  154. - dataFileList
  155. {
  156.     return dataFileList;
  157. }
  158.  
  159. - addDataFile:data
  160. {
  161.     if(![data isKindOf:[WaisDataFile class]]) return nil;
  162.     [dataFileList addObjectIfAbsent:data];
  163.     return self;
  164. }
  165.  
  166. - removeDataFile:data
  167. {
  168.     return [dataFileList removeObject:data];
  169. }
  170.  
  171. - clearDataFiles
  172. {
  173.     //!!! We aren't sharing data files, so free them.
  174.     [[dataFileList freeObjects] empty];
  175.     return self;
  176. }
  177.  
  178. - (FILE *)connection
  179. {
  180.     return connection;
  181. }
  182.  
  183. - (BOOL)isConnected
  184. {
  185.    return isConnected;
  186. }
  187.  
  188. - (long)bufferLength
  189. {
  190.     return bufferLength;
  191. }
  192.  
  193. - copyConnection:(FILE *)theConnection length:(long)theBufferLength
  194. {
  195.     connection = theConnection;
  196.     bufferLength = theBufferLength;
  197.     return self;
  198. }
  199.  
  200. - setConnected:(BOOL)yn;
  201. {
  202.     int i, n;
  203.     id s;
  204.     const char *server, *service, *otherServer, *otherService;
  205.     char hostname[512];
  206.     static char request[MAX_MESSAGE_LEN], response[MAX_MESSAGE_LEN];
  207.     
  208.     // Lock to prevent conflict with network transactions in other threads.
  209.     [Wais lockTransaction];
  210.  
  211.     // When disconnecting, make sure no other source is using connection.
  212.     if(!yn)
  213.     {
  214.     if(!isConnected)
  215.     {
  216.         connection = NULL;
  217.         [Wais unlockTransaction];
  218.         return self;
  219.     }
  220.     n = [[WaisSource waisObjectList] count];
  221.     for(i=0; i<n; i++)
  222.         if(self!=(s=[[WaisSource waisObjectList] objectAt:i])
  223.             && [s isKindOf:[WaisSource class]]
  224.             && connection==[s connection])
  225.             break;
  226.     if(i >= n) close_connection(connection);
  227.     connection = NULL;
  228.     isConnected = NO;
  229.     [Wais unlockTransaction];
  230.     return self;
  231.     }
  232.     
  233.     // Establish connection.  Don't connect to local host.
  234.     if(isConnected) { [Wais unlockTransaction]; return self; }
  235.     bufferLength = MAX_MESSAGE_LEN;
  236.     gethostname(hostname, 512); hostname[512-1] = 0;
  237.     server = [self valueForStringKey:":ip-name"];
  238.     if(!server || strlen(server)==0)
  239.         server = [self valueForStringKey:":ip-address"];
  240.     if(server && (strlen(server)==0
  241.         || strcmp(server, hostname)==0 || strcmp(server, "localhost")==0))
  242.         server = 0;
  243.     service = [self valueForStringKey:":tcp-port"];
  244.     if(service && strlen(service)==0) service = 0;
  245.     if(!server || !service) connection = NULL;
  246.     else if(!(connection = connect_to_server(server, atoi(service))))
  247.     {
  248.     [Wais unlockTransaction];
  249.         ErrorMsg(errorTitle, "Can't connect to server %s.", server);
  250.     return nil;
  251.     }
  252.     
  253.     // Send initialization message, giving info about user.
  254.     if((bufferLength=init_connection(request, response,
  255.         MAX_MESSAGE_LEN, connection, (userInfo?userInfo:"anonymous"))) < 0)
  256.     {
  257.     [Wais unlockTransaction];
  258.         ErrorMsg(errorTitle, "Can't connect to server %s.",
  259.         (server ? server : "localhost"));
  260.     connection = NULL; return nil;
  261.     }
  262.     isConnected = YES;
  263.  
  264.     // Let other sources with same host and port know they're ready.
  265.     n = [[WaisSource waisObjectList] count];
  266.     if(server && service) for(i=0; i<n; i++)
  267.     {
  268.         s = [[WaisSource waisObjectList] objectAt:i];
  269.     if(![s isKindOf:[WaisSource class]]) continue;
  270.     if(!(otherServer=[s valueForStringKey:":ip-address"])
  271.             && !(otherServer=[s valueForStringKey:":ip-name"]))
  272.         continue;
  273.     if(!(otherService=[s valueForStringKey:":tcp-port"]))
  274.         continue;
  275.     if(strcmp(otherServer, server)!=0
  276.         || strcmp(otherService, service)!=0)
  277.         continue;
  278.     [s copyConnection:connection length:bufferLength];
  279.     }
  280.     [Wais unlockTransaction];
  281.     return self;
  282. }
  283.  
  284. - (short)readWaisStruct:(const char *)structName
  285.     forElement:(const char *)elementName
  286.     fromFile:(FILE *)file
  287.     withDecoder:(WaisDecoder)theDecoder
  288. {
  289.     const char *config, *tail;
  290.     char value[STRINGSIZE+MAX_SYMBOL_SIZE];
  291.     short check_result;
  292.     id data;
  293.     
  294.     // Decode data file entry into list.
  295.     if(0 == strcmp(elementName, ":index-list"))
  296.     {
  297.     data = [[WaisDataFile alloc] initKey:NULL];
  298.     check_result = [data readWaisStruct:structName
  299.         forElement:elementName fromFile:file withDecoder:theDecoder];
  300.     if(check_result==FALSE || check_result==END_OF_STRUCT_OR_LIST)
  301.         { [data free]; return check_result; }
  302.     [dataFileList addObject:data];
  303.     return TRUE;
  304.     }
  305.  
  306.     // Else do standard read.
  307.     check_result = [super readWaisStruct:structName
  308.     forElement:elementName fromFile:file withDecoder:theDecoder];
  309.  
  310.     // Hack ugly special case of ":configuration" field.
  311.     if(0==strcmp(structName, ":source")
  312.         && (config=[self valueForStringKey:":configuration"]))
  313.     {
  314.     read_subfield(config, "IPAddress", value, STRINGSIZE);
  315.     [self insertStringKey:":ip-address" value:value];
  316.     read_subfield(config, "RemotePort", value, STRINGSIZE);
  317.     [self insertStringKey:":tcp-port" value:value];
  318.     [self insertStringKey:":configuration" value:NULL];
  319.     }
  320.  
  321.     // Match key w/ info, if full src record has been read.
  322.     if(0 == strcmp(structName, [WaisSource fileStructName]))
  323.     {
  324.         if(!key) [self setKey:[self valueForStringKey:":filename"]];
  325.     else if(![self valueForStringKey:":filename"])
  326.     {
  327.         tail = strrchr(key, '/');
  328.         if(tail) tail++;
  329.         else tail = key;
  330.         [self insertStringKey:":filename" value:tail];
  331.     }
  332.     }    
  333.     return check_result;
  334. }
  335.  
  336. - readWaisFile
  337. {
  338.     [self setConnected:NO];
  339.     if(![super readWaisFile]) return nil;
  340.     return self;
  341. }
  342.  
  343. - (short)writeWaisStruct:(const char *)structName
  344.     forElement:(const char *)elementName
  345.     toFile:(FILE *)file
  346.     withDecoder:(WaisDecoder)theDecoder
  347. {
  348.     id data;
  349.     short check_result;
  350.     
  351.     // Check if need next subobject in a list to extract data from.
  352.     // The listCounter keeps track of where we are in list.
  353.     if(0 == strcmp(elementName, ":index-list"))
  354.     {
  355.         data = [dataFileList objectAt:listCounter];
  356.     if(!data) { listCounter = 0; return END_OF_STRUCT_OR_LIST; }
  357.     else listCounter++;
  358.     check_result = [data writeWaisStruct:structName
  359.         forElement:elementName toFile:file withDecoder:theDecoder];
  360.     if(check_result==FALSE || check_result==END_OF_STRUCT_OR_LIST)
  361.         listCounter = 0;
  362.     return check_result;
  363.     }
  364.     
  365.     // Else do normal write (clearing list counter).
  366.     listCounter = 0;
  367.     return [super writeWaisStruct:structName
  368.     forElement:elementName toFile:file withDecoder:theDecoder];
  369. }
  370.  
  371. - writeWaisFile
  372. {
  373.     char buf[1024];
  374.     
  375.     // Fill in any missing essential fields with default values first.
  376.     if(![self valueForStringKey:":cost"]
  377.         || strlen([self valueForStringKey:":cost"])==0)
  378.     [self insertStringKey:":cost" value:"0.00"];
  379.     if(![self valueForStringKey:":cost-unit"]
  380.         || strlen([self valueForStringKey:":cost-unit"])==0)
  381.     [self insertStringKey:":cost-unit" value:":free"];
  382.     if(![self valueForStringKey:":description"]
  383.     || strlen([self valueForStringKey:":description"])==0)
  384.     {
  385.     sprintf(buf, "Created with Objective-C WAIS by %s on %s.",
  386.         current_user_name(), printable_time());
  387.     [self insertStringKey:":description" value:buf];
  388.     }
  389.     return [super writeWaisFile];
  390. }
  391.  
  392. @end
  393.  
  394.  
  395. @implementation WaisDataFile
  396.  
  397. + (const char *)fileStructName
  398. {
  399.     return ":include";
  400. }
  401.  
  402. + (WaisDecoder)fileStructDecoder
  403. {
  404.     return waisDataFileDecoder;
  405. }
  406.  
  407. @end
  408.